//package java_cup.runtime;

//import javax.xml.stream.XMLStreamException;
//import javax.xml.stream.System.Xml.XmlWriter;

/**
 * Default Implementation for SymbolFactory, creates plain old Symbols
 *
 * @version last updated 27-03-2006
 * @author Michael Petter
 */

/*
 * ************************************************* class DefaultSymbolFactory
 * interface for creating new EmitSymbols
 ***************************************************/
public class ComplexSymbolFactory : SymbolFactory {
    public class Location {
        public String unit = "unknown";
        public int line, column;
        public int offset = -1;

        /**
         * Copy Constructor for other ComplexSymbolFactory based Locations
         * 
         * @param other
         */
        public Location(Location other)
            : this(other.unit, other.line, other.column, other.offset) {
        }

        /**
         * Location Object stores compilation unit, line, column and offset to the file
         * start
         * 
         * @param unit   compilation unit, e.g. file name
         * @param line   line Count
         * @param column column Count
         * @param offset offset from file start
         */
        public Location(String unit, int line, int column, int offset)
            : this(unit, line, column) {
            this.offset = offset;
        }

        /**
         * Location Object stores compilation unit, line and column
         * 
         * @param unit   compilation unit, e.g. file name
         * @param line   line Count
         * @param column column Count
         */
        public Location(String unit, int line, int column) {
            this.unit = unit;
            this.line = line;
            this.column = column;
        }

        /**
         * Location Object stores line, column and offset to the file start
         * 
         * @param line   line Count
         * @param column column Count
         * @param offset offset from file start
         */
        public Location(int line, int column, int offset)
            : this(line, column) {
            this.offset = offset;
        }

        /**
         * Location Object stores line and column
         * 
         * @param line   line Count
         * @param column column Count
         */
        public Location(int line, int column) {
            this.line = line;
            this.column = column;
        }

        /**
         * getColumn
         * 
         * @returns column if known, else -1
         */
        public int Column => column;

        /**
         * getLine
         * 
         * @returns line if known, else -1
         */
        public int Line => line;

        /**
         * move moves this Location by the given differences.
         * 
         * @param linediff
         * @param coldiff
         * @param offsetdiff
         */
        public void move(int linediff, int coldiff, int offsetdiff) {
            if (line >= 0)
                line += linediff;
            if (column >= 0)
                column += coldiff;
            if (offset >= 0)
                offset += offsetdiff;
        }

        /**
         * Cloning factory method
         * 
         * @param other
         * @return new cloned Location
         */
        public static Location clone(Location other) {
            return new Location(other);
        }

        /**
         * getUnit
         * 
         * @returns compilation unit if known, else 'unknown'
         */
        public String Unit => unit;

        /**
         * getLine
         * 
         * @returns line if known, else -1
         */
        /*@Override*/
        public override string ToString() {
            return Unit + ":" + Line + "/" + Column + "(" + offset + ")";
        }

        /**
         * Writes the location information directly into an XML document
         * 
         * @param writer      the destination XML Document
         * @param orientation adds details about the orientation of this location as an
         *                    attribute; often used with the strings "_left" or "_right"
         * @throws XMLStreamException
         */
        public void toXML(System.Xml.XmlWriter writer, String orientation) /*throws XMLStreamException*/ {
            writer.WriteStartElement("location");
            writer.WriteAttributeString("compilationunit", unit);
            writer.WriteAttributeString("orientation", orientation);
            writer.WriteAttributeString("linenumber", line + "");
            writer.WriteAttributeString("columnnumber", column + "");
            writer.WriteAttributeString("offset", offset + "");
            writer.WriteEndElement();
        }

        /**
         * getOffset
         * 
         * @returns offset to start if known, else -1
         */
        public int Offset => offset;
    }

    /**
     * ComplexSymbol with detailed Location Informations and a Name
     */
    public class ComplexSymbol : Node {
        public String name;
        public Location xleft, xright;

        public ComplexSymbol(String name, int id)
            : base(id) {
            this.name = name;
        }

        public ComplexSymbol(String name, int id, Object value)
            : base(id, value) {
            this.name = name;
        }

        /*@Override*/
        public override string ToString() {
            if (xleft == null || xright == null)
                return "TheSymbolDraft: " + name;
            return "TheSymbolDraft: " + name + " (" + xleft + " - " + xright + ")";
        }

        public String Name => name;

        public ComplexSymbol(String name, int id, int state)
            : base(id, state) {
            this.name = name;
        }

        public ComplexSymbol(String name, int id, Node left, Node right)
            : base(id, left, right) {
            this.name = name;
            if (left != null)
                xleft = ((ComplexSymbol)left).xleft;
            if (right != null)
                xright = ((ComplexSymbol)right).xright;
        }

        public ComplexSymbol(String name, int id, Location left, Location right)
            : base(id, left.offset, right.offset) {
            this.name = name;
            xleft = left;
            xright = right;
        }

        public ComplexSymbol(String name, int id, Node left, Node right, Object value)
            : base(id, left._left, right._right, value) {
            this.name = name;
            if (left != null)
                xleft = ((ComplexSymbol)left).xleft;
            if (right != null)
                xright = ((ComplexSymbol)right).xright;
        }

        public ComplexSymbol(String name, int id, Node left, Object value)
            : base(id, left._right, left._right, value) {
            this.name = name;
            if (left != null) {
                xleft = ((ComplexSymbol)left).xright;
                xright = ((ComplexSymbol)left).xright;
            }
        }

        public ComplexSymbol(String name, int id, Location left, Location right, Object value)
            : base(id, left.offset, right.offset, value) {
            this.name = name;
            xleft = left;
            xright = right;
        }

        public Location Left => xleft;

        public Location Right => xright;
    }

    // Factory methods
    /**
     * newSymbol creates a complex SymbolDraft with Location objects for _left and _right
     * boundaries; this is used for terminals with values!
     */
    public Node newSymbol(String name, int id, Location left, Location right, Object value) {
        return new ComplexSymbol(name, id, left, right, value);
    }

    /**
     * newSymbol creates a complex SymbolDraft with Location objects for _left and _right
     * boundaries; this is used for terminals without values!
     */
    public Node newSymbol(String name, int id, Location left, Location right) {
        return new ComplexSymbol(name, id, left, right);
    }
    /*@Override*/
    public Node newSymbol(String name, int id, Node left, Object value) {
        return new ComplexSymbol(name, id, left, value);
    }
    /*@Override*/
    public Node newSymbol(String name, int id, Node left, Node right, Object value) {
        return new ComplexSymbol(name, id, left, right, value);
    }
    /*@Override*/
    public Node newSymbol(String name, int id, Node left, Node right) {
        return new ComplexSymbol(name, id, left, right);
    }
    /*@Override*/
    public Node newSymbol(String name, int id) {
        return new ComplexSymbol(name, id);
    }
    /*@Override*/
    public Node newSymbol(String name, int id, Object value) {
        return new ComplexSymbol(name, id, value);
    }
    /*@Override*/
    public Node startSymbol(String name, int id, int state) {
        return new ComplexSymbol(name, id, state);
    }
}
